/*
 * Copyright 2010-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.gitee.kamismile.akka.core;

import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import com.gitee.kamismile.akka.di.SpringExtension;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.*;
import org.springframework.beans.factory.support.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.env.Environment;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.Map;
import java.util.Optional;

import static org.springframework.util.Assert.notNull;

public class StoneAkkaScannerConfigurer
        implements BeanDefinitionRegistryPostProcessor, InitializingBean, EnvironmentAware,ApplicationContextAware {

    private String basePackage;

    private ApplicationContext applicationContext;

    private Environment environment;


    /**
     * This property lets you set the base package for your mapper interface files.
     * <p>
     * You can set more than one package by using a semicolon or comma as a separator.
     * <p>
     * Mappers will be searched for recursively starting in the specified package(s).
     *
     * @param basePackage base package name
     */
    public void setBasePackage(String basePackage) {
        this.basePackage = basePackage;
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        notNull(this.basePackage, "Property 'basePackage' is required");
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        if (!(beanFactory instanceof DefaultListableBeanFactory)) {
            throw new IllegalStateException(
                    "stoneAkka needs to operate on a DefaultListableBeanFactory");
        }
        DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;
        String[] baseActorBeanNames = dlbf.getBeanNamesForType(BaseActor.class);

        if (baseActorBeanNames.length == 0) {
            return;
        }
        ActorSystem actorSystem = this.applicationContext.getBean(ActorSystem.class);
        SpringExtension springExtension = this.applicationContext.getBean(SpringExtension.class);
        for (String baseActorBeanName : baseActorBeanNames) {
            if (baseActorBeanName.contains(".")) {
                continue;
            }

            ActorRef actorRef = actorSystem.actorOf(
                    springExtension.props(baseActorBeanName,applicationContext), baseActorBeanName + "RealActor");
            dlbf.registerSingleton(baseActorBeanName + "ActorRef",
                    actorRef);
        }


    }
    /**
     * {@inheritDoc}
     *
     * @since 1.0.2
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        ClassPathStoneAkkaScanner scanner = new ClassPathStoneAkkaScanner(registry);
        scanner.setResourceLoader(this.applicationContext);
//    scanner.setBeanNameGenerator(new AnnotationBeanNameGenerator(){
//      @Override
//      public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
//        return super.generateBeanName(definition, registry)+"AkkaRef";
//      }
//    });
//        scanner.registerFilters();
        scanner.setEnvironment(this.environment);
        scanner.addIncludeFilter(new AssignableTypeFilter(BaseActor.class));
        scanner.scan(
                StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;

    }
}
